//
// Created by Chiro on 2022/10/2.
//
#include <ASK.hpp>
#include <signal.h>
#include <cstdlib>
#include <ctime>
#include <sys/time.h>

ASK *top = nullptr;
VerilatedFstC *tfp = nullptr;
struct timeval start {};
struct timeval end {};
uint64_t cnt = 0;

void exit_normally(int no) {
  top->final();
  IFDEF(CONFIG_XM_DUMP_WAVE, tfp->close());
  delete top;
  gettimeofday(&end, nullptr);
  double timer = (double) (1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec) / 1000000;
  double simu_speed_khz = (double) cnt / 2 / 1000 / timer;
  double simu_speed_mhz = simu_speed_khz / 1000;
  if (simu_speed_mhz > 1) {
    Log("simulation freq \tis %lf mhz\n", simu_speed_mhz);
  } else {
    Log("simulation freq \tis %lf khz\n", simu_speed_khz);
  }
  Log("Exit normally.");
  exit(0);
}

static inline void x_bind_signal() {
  signal(SIGTSTP, exit_normally);
  signal(SIGTERM, exit_normally);
  signal(SIGINT, exit_normally);
}

int main(int argc, char **argv, char **env) {
  Verilated::commandArgs(argc, argv);
  Verilated::traceEverOn(true);
  x_bind_signal();
  srand(time(NULL));
  top = new ASK;
#ifdef CONFIG_XM_DUMP_WAVE
  tfp = new VerilatedFstC();
  top->trace(tfp, 0);
  tfp->open("ASK.fst");
#endif
  srand(time(nullptr));
  const uint64_t cnt_max = -1;
  auto exec = [&](int n) {
    while (n--) {
      top->clock = 0;
      top->eval();
      IFDEF(CONFIG_XM_DUMP_WAVE, tfp->dump(cnt));
      top->clock = 1;
      top->eval();
      IFDEF(CONFIG_XM_DUMP_WAVE, tfp->dump(cnt + 1));
      cnt += 2;
    }
  };

  top->io_clockOffset = 0;
  uint64_t dataSource = 0xaaaaaaaa55555550;

  top->reset = 1;
  exec(5);
  top->reset = 0;
  int offset = 0;
  int total = CONFIG_XM_ASK_CLK_PER_BIT;
  gettimeofday(&start, nullptr);
  while (!Verilated::gotFinish() && cnt / 2 < cnt_max) {
    top->io_start = 0;
    top->io_dataSource = dataSource;
    top->io_clockOffset = CONFIG_XM_ASK_CLK_PER_BIT * offset / total;
    exec(rand() % (32 * CONFIG_XM_ASK_CLK_PER_BIT));
    exec(CONFIG_XM_ASK_CLK_PER_BIT * 2);
    top->io_start = 1;
    exec(CONFIG_XM_ASK_CLK_PER_BIT * (8 + 72 + 1));
    if (top->io_dacOut != dataSource) {
      Err("[%d] Result check failed! DUT: %016lx, REF: %016lx", cnt / 2, top->io_dacOut, dataSource);
      exit_normally(0);
    } else {
      // Log("[%d] pass data 0x%016lx", cnt / 2, dataSource);
    }
    dataSource++;
    offset = rand() % total;
  }
  if (cnt == cnt_max) {
    Log("Warning: reach cnt MAX(%lu)", cnt_max);
  }
  exit_normally(0);
  return 0;
}
